package cn.yanceysong.gitstats.analyzer;

import cn.yanceysong.gitstats.config.GitStatsConfig;
import cn.yanceysong.gitstats.model.*;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.LogCommand;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
import org.eclipse.jgit.util.io.DisabledOutputStream;

import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.*;
import java.util.regex.Pattern;

/**
 * Git仓库统计分析器
 */
@Slf4j
public class GitStatsAnalyzer {
    
    private static final Pattern FILE_EXTENSION_PATTERN = Pattern.compile("\\.([^.]+)$");
    private static final Set<String> EXCLUDED_EXTENSIONS = Set.of(
        "class", "jar", "war", "ear", "zip", "tar", "gz", "7z", "rar",
        "png", "jpg", "jpeg", "gif", "bmp", "ico", "svg", "webp",
        "mp4", "avi", "mov", "wmv", "flv", "mp3", "wav", "ogg",
        "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx"
    );
    
    private final File repositoryPath;
    private final GitStatsConfig config;
    
    public GitStatsAnalyzer(File repositoryPath) {
        this(repositoryPath, GitStatsConfig.builder().build());
    }
    
    public GitStatsAnalyzer(File repositoryPath, GitStatsConfig config) {
        this.repositoryPath = repositoryPath;
        this.config = config;
    }

    /**
     * 分析Git仓库统计信息
     */
    public GitStatsResult analyze() throws Exception {
        log.info("开始分析Git仓库: {}", repositoryPath.getAbsolutePath());
        
        try (Git git = Git.open(repositoryPath)) {
            Repository repository = git.getRepository();
            
            GitStatsResult result = GitStatsResult.builder()
                .repositoryPath(repositoryPath.getAbsolutePath())
                .startTime(LocalDateTime.now())
                .build();
            
            Map<String, ContributorStats> contributorMap = new HashMap<>();
            
            // 获取所有提交
            LogCommand logCommand = git.log().all();

            // 设置日期过滤器
            if (config.getSince() != null || config.getUntil() != null) {
                RevFilter dateFilter = createDateFilter(config.getSince(), config.getUntil());
                if (dateFilter != null) {
                    logCommand = logCommand.setRevFilter(dateFilter);
                }
            }
            
            Iterable<RevCommit> commits = logCommand.call();
            
            for (RevCommit commit : commits) {
                if (shouldSkipCommit(commit)) {
                    continue;
                }
                
                processCommit(repository, commit, contributorMap);
            }
            
            // 构建结果
            result.setContributors(new ArrayList<>(contributorMap.values()));
            result.setContributorMap(contributorMap);
            result.setEndTime(LocalDateTime.now());

            // 计算总体统计
            calculateTotalStats(result);

            log.info("分析完成，共处理 {} 个提交，{} 个贡献者",
                result.getTotalCommits(), result.getTotalContributors());
            
            return result;
        }
    }
    
    /**
     * 处理单个提交
     */
    private void processCommit(Repository repository, RevCommit commit, Map<String, ContributorStats> contributorMap) throws IOException {
        PersonIdent author = commit.getAuthorIdent();
        String email = author.getEmailAddress();
        String name = author.getName();
        
        // 获取或创建贡献者统计
        ContributorStats contributor = contributorMap.computeIfAbsent(email, 
            k -> ContributorStats.builder()
                .email(email)
                .name(name)
                .build());
        
        contributor.incrementCommitCount();
        
        // 更新提交时间
        LocalDateTime commitTime = LocalDateTime.ofInstant(
            author.getWhen().toInstant(), 
            ZoneId.systemDefault()
        );
        contributor.updateCommitTime(commitTime);
        
        // 计算代码行数变化
        CommitStats commitStats = calculateCommitStats(repository, commit);
        
        // 更新贡献者统计
        contributor.addStats(commitStats.getAddedLines(), commitStats.getDeletedLines(), commitStats.getModifiedLines());
        
        // 更新每日统计
        String date = commitTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        DailyStats dailyStats = contributor.getOrCreateDailyStats(date);
        dailyStats.addStats(commitStats.getAddedLines(), commitStats.getDeletedLines(), commitStats.getModifiedLines());
        dailyStats.incrementCommitCount();
        
        // 更新文件类型统计
        for (Map.Entry<String, FileTypeStats> entry : commitStats.getFileTypeStats().entrySet()) {
            FileTypeStats fileTypeStats = contributor.getOrCreateFileTypeStats(entry.getKey());
            fileTypeStats.addStats(entry.getValue().getAddedLines(), 
                entry.getValue().getDeletedLines(), 
                entry.getValue().getModifiedLines());
        }
        
        log.debug("处理提交: {} - {} (+{} -{} ~{})", 
            commit.getName().substring(0, 8), 
            commit.getShortMessage(),
            commitStats.getAddedLines(),
            commitStats.getDeletedLines(),
            commitStats.getModifiedLines());
    }
    
    /**
     * 计算提交的代码行数统计
     */
    private CommitStats calculateCommitStats(Repository repository, RevCommit commit) throws IOException {
        CommitStats stats = new CommitStats();
        
        try (RevWalk revWalk = new RevWalk(repository);
             ObjectReader reader = repository.newObjectReader();
             DiffFormatter diffFormatter = new DiffFormatter(DisabledOutputStream.INSTANCE)) {
            
            diffFormatter.setRepository(repository);
            diffFormatter.setDetectRenames(true);
            
            RevCommit parent = null;
            if (commit.getParentCount() > 0) {
                parent = revWalk.parseCommit(commit.getParent(0).getId());
            }
            
            List<DiffEntry> diffs;
            if (parent != null) {
                diffs = diffFormatter.scan(parent.getTree(), commit.getTree());
            } else {
                // 首次提交，与空树比较
                diffs = diffFormatter.scan(new EmptyTreeIterator(),
                    new CanonicalTreeParser(null, reader, commit.getTree()));
            }

            for (DiffEntry diff : diffs) {
                String filePath = diff.getNewPath() != null ? diff.getNewPath() : diff.getOldPath();
                if (shouldSkipFile(filePath)) {
                    continue;
                }

                String fileType = getFileType(filePath);
                FileTypeStats fileTypeStats = stats.getFileTypeStats().computeIfAbsent(fileType,
                    k -> FileTypeStats.builder().fileType(fileType).build());

                // 计算文件的行数变化
                int added = 0, deleted = 0, modified = 0;

                if (diff.getChangeType() == DiffEntry.ChangeType.ADD) {
                    added = countLines(reader, diff.getNewId().toObjectId());
                    fileTypeStats.addStats(added, 0, 0);
                    fileTypeStats.incrementFileCount();
                } else if (diff.getChangeType() == DiffEntry.ChangeType.DELETE) {
                    deleted = countLines(reader, diff.getOldId().toObjectId());
                    fileTypeStats.addStats(0, deleted, 0);
                } else if (diff.getChangeType() == DiffEntry.ChangeType.MODIFY) {
                    // 对于修改的文件，我们需要更精确的计算
                    DiffStats diffStats = calculateDiffStats(repository, diff);
                    added = diffStats.getAddedLines();
                    deleted = diffStats.getDeletedLines();
                    modified = diffStats.getModifiedLines();
                    fileTypeStats.addStats(added, deleted, modified);
                } else if (diff.getChangeType() == DiffEntry.ChangeType.RENAME ||
                          diff.getChangeType() == DiffEntry.ChangeType.COPY) {
                    // 重命名或复制文件，计算内容变化
                    DiffStats diffStats = calculateDiffStats(repository, diff);
                    added = diffStats.getAddedLines();
                    deleted = diffStats.getDeletedLines();
                    modified = diffStats.getModifiedLines();
                    fileTypeStats.addStats(added, deleted, modified);
                }

                stats.addStats(added, deleted, modified);
            }
        }
        
        return stats;
    }
    
    /**
     * 计算文件差异统计
     */
    private DiffStats calculateDiffStats(Repository repository, DiffEntry diff) throws IOException {
        DiffStats stats = new DiffStats();

        // 这里简化处理，实际应该解析diff内容
        // 为了简化，我们使用文件总行数作为近似值
        try (ObjectReader reader = repository.newObjectReader()) {
            if (!diff.getNewId().equals(DiffEntry.DEV_NULL)) {
                int newLines = countLines(reader, diff.getNewId().toObjectId());
                if (!diff.getOldId().equals(DiffEntry.DEV_NULL)) {
                    int oldLines = countLines(reader, diff.getOldId().toObjectId());
                    if (newLines > oldLines) {
                        stats.setAddedLines(newLines - oldLines);
                    } else if (newLines < oldLines) {
                        stats.setDeletedLines(oldLines - newLines);
                    } else {
                        stats.setModifiedLines(newLines);
                    }
                } else {
                    stats.setAddedLines(newLines);
                }
            } else if (!diff.getOldId().equals(DiffEntry.DEV_NULL)) {
                stats.setDeletedLines(countLines(reader, diff.getOldId().toObjectId()));
            }
        }
        
        return stats;
    }
    
    /**
     * 计算文件行数
     */
    private int countLines(ObjectReader reader, ObjectId objectId) throws IOException {
        if (objectId.equals(ObjectId.zeroId())) {
            return 0;
        }

        try {
            byte[] data = reader.open(objectId).getBytes();
            String content = new String(data);
            if (content.isEmpty()) {
                return 0;
            }
            // 计算行数，如果文件不以换行符结尾，需要加1
            int lines = content.split("\n", -1).length;
            if (!content.endsWith("\n")) {
                lines--;
            }
            return Math.max(1, lines);
        } catch (Exception e) {
            // 忽略无法读取的文件（如二进制文件）
            log.debug("无法读取文件: {}", objectId.getName());
            return 0;
        }
    }
    
    /**
     * 获取文件类型
     */
    private String getFileType(String filePath) {
        if (filePath == null || filePath.isEmpty()) {
            return "unknown";
        }
        
        java.util.regex.Matcher matcher = FILE_EXTENSION_PATTERN.matcher(filePath);
        if (matcher.find()) {
            return matcher.group(1).toLowerCase();
        }
        
        return "no-extension";
    }
    
    /**
     * 判断是否应该跳过文件
     */
    private boolean shouldSkipFile(String filePath) {
        if (filePath == null || filePath.isEmpty()) {
            return true;
        }
        
        // 跳过二进制文件
        String fileType = getFileType(filePath);
        if (EXCLUDED_EXTENSIONS.contains(fileType)) {
            return true;
        }
        
        // 跳过配置文件中的文件
        if (config.getExcludedPaths() != null) {
            for (String excludedPath : config.getExcludedPaths()) {
                if (filePath.contains(excludedPath)) {
                    return true;
                }
            }
        }
        
        return false;
    }
    
    /**
     * 判断是否应该跳过提交
     */
    private boolean shouldSkipCommit(RevCommit commit) {
        // 跳过合并提交（如果配置要求）
        if (config.isSkipMergeCommits() && commit.getParentCount() > 1) {
            return true;
        }
        
        // 跳过空提交（检查是否有父提交且树相同）
        if (commit.getParentCount() > 0) {
            try {
                RevCommit parent = commit.getParent(0);
                if (commit.getTree().getId().equals(parent.getTree().getId())) {
                    return true;
                }
            } catch (Exception e) {
                log.debug("检查空提交时出错: {}", e.getMessage());
            }
        }
        
        return false;
    }

    /**
     * 计算总体统计信息
     */
    private void calculateTotalStats(GitStatsResult result) {
        int totalCommits = 0;
        int totalAddedLines = 0;
        int totalDeletedLines = 0;
        int totalModifiedLines = 0;

        for (ContributorStats contributor : result.getContributors()) {
            totalCommits += contributor.getCommitCount();
            totalAddedLines += contributor.getAddedLines();
            totalDeletedLines += contributor.getDeletedLines();
            totalModifiedLines += contributor.getModifiedLines();
        }

        result.setTotalCommits(totalCommits);
        result.setTotalContributors(result.getContributors().size());
        result.setTotalAddedLines(totalAddedLines);
        result.setTotalDeletedLines(totalDeletedLines);
        result.setTotalModifiedLines(totalModifiedLines);
        result.setTotalLines(totalAddedLines + totalModifiedLines);
    }

    /**
     * 提交统计内部类
     */
    private static class CommitStats {
        private int addedLines = 0;
        private int deletedLines = 0;
        private int modifiedLines = 0;
        private Map<String, FileTypeStats> fileTypeStats = new HashMap<>();
        
        public void addStats(int added, int deleted, int modified) {
            this.addedLines += added;
            this.deletedLines += deleted;
            this.modifiedLines += modified;
        }
        
        // Getters and setters
        public int getAddedLines() { return addedLines; }
        public int getDeletedLines() { return deletedLines; }
        public int getModifiedLines() { return modifiedLines; }
        public Map<String, FileTypeStats> getFileTypeStats() { return fileTypeStats; }
    }

    /**
     * 创建日期过滤器
     */
    private RevFilter createDateFilter(LocalDateTime since, LocalDateTime until) {
        if (since == null && until == null) {
            return null;
        }

        if (since != null && until != null) {
            // 转换为 Date 对象
            Date sinceDate = Date.from(since.atZone(ZoneId.systemDefault()).toInstant());
            Date untilDate = Date.from(until.atZone(ZoneId.systemDefault()).toInstant());
            return CommitTimeRevFilter.between(sinceDate, untilDate);
        } else if (since != null) {
            // 只有开始时间
            Date sinceDate = Date.from(since.atZone(ZoneId.systemDefault()).toInstant());
            return CommitTimeRevFilter.after(sinceDate);
        } else {
            // 只有结束时间
            Date untilDate = Date.from(until.atZone(ZoneId.systemDefault()).toInstant());
            return CommitTimeRevFilter.before(untilDate);
        }
    }

    /**
     * 差异统计内部类
     */
    private static class DiffStats {
        private int addedLines = 0;
        private int deletedLines = 0;
        private int modifiedLines = 0;
        
        // Getters and setters
        public int getAddedLines() { return addedLines; }
        public void setAddedLines(int addedLines) { this.addedLines = addedLines; }
        public int getDeletedLines() { return deletedLines; }
        public void setDeletedLines(int deletedLines) { this.deletedLines = deletedLines; }
        public int getModifiedLines() { return modifiedLines; }
        public void setModifiedLines(int modifiedLines) { this.modifiedLines = modifiedLines; }
    }
}
